home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 March / Macworld (1998-03) (Disk 1).dmg / Shareware World / Info / For Developers / GhostScript 5.10 / MacGS-510 / files / ps2ascii.ps < prev    next >
Text File  |  1996-07-09  |  40KB  |  1,311 lines

  1. %    Copyright (C) 1991, 1995, 1996 Aladdin Enterprises.  All rights reserved.
  2. % This file is part of Aladdin Ghostscript.
  3. % Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  4. % or distributor accepts any responsibility for the consequences of using it,
  5. % or for whether it serves any particular purpose or works at all, unless he
  6. % or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  7. % License (the "License") for full details.
  8. % Every copy of Aladdin Ghostscript must include a copy of the License,
  9. % normally in a plain ASCII text file named PUBLIC.  The License grants you
  10. % the right to copy, modify and redistribute Aladdin Ghostscript, but only
  11. % under certain conditions described in the License.  Among other things, the
  12. % License requires that the copyright notice and this notice be preserved on
  13. % all copies.
  14.  
  15. % Extract the ASCII text from a PostScript file.  Nothing is displayed.
  16. % Instead, ASCII information is written to stdout.  The idea is similar to
  17. % Glenn Reid's `distillery', only a lot more simple-minded, and less robust.
  18.  
  19. % If SIMPLE is defined, just the text is written, with a guess at line
  20. % breaks and word spacing.  If SIMPLE is not defined, lines are written
  21. % to stdout as follows:
  22. %
  23. %    F <height> <width> (<fontname>)
  24. %        Indicate the font height and the width of a space.
  25. %
  26. %    P
  27. %        Indicate the end of the page.
  28. %    S <x> <y> (<string>) <width>
  29. %        Display a string.
  30. %
  31. % <width> and <height> are integer dimensions in units of 1/720".
  32. % <x> and <y> are integer coordinates, in units of 1/720", with the origin
  33. %   at the lower left.
  34. % <string> and <fontname> are strings represented with the standard
  35. %   PostScript escape conventions.
  36.  
  37. % If COMPLEX is defined, the following additional types of lines are
  38. % written to stdout.
  39. %
  40. %    C <r> <g> <b>
  41. %        Indicate the current color.
  42. %
  43. %    I <x> <y> <width> <height>
  44. %        Note the presence of an image.
  45. %
  46. %    R <x> <y> <width> <height>
  47. %        Fill a rectangle.
  48. %
  49. % <r>, <g>, and <b> are RGB values expressed as integers between 0 and 1000.
  50. %
  51. % Note that future versions of this program (in COMPLEX mode) may add
  52. %   other output elements, so programs parsing the output should be
  53. %   prepared to ignore elements that they do not recognize.
  54.  
  55. % Note that this code will only work in all cases if systemdict is writable
  56. % and if `binding' the definitions of operators defined as procedures
  57. % is deferred.  For this reason, it is normally invoked with
  58. %    gs -q -dNODISPLAY -dNOBIND -dWRITESYSTEMDICT ps2ascii.ps
  59.  
  60. % Thanks to:
  61. %    J Greely <jgreely@cis.ohio-state.edu> for improvements to this code;
  62. %    Jerry Whelan <jerryw@abode.ccd.bnl.gov> for motivating other improvements;
  63. %    David M. Jones <dmjones@theory.lcs.mit.edu> for improvements noted below.
  64.  
  65. %%* Modifications to ps2ascii.ps by David M. Jones
  66. %%* (dmjones@theory.lcs.mit.edu), June 25-July 8, 1996
  67.  
  68. %%* Modifications:
  69. %%* 
  70. %%* (a) added code to give better support for dvips files by providing
  71. %%*     FontBBox's, FontName's and Encoding vectors for downloaded
  72. %%*     bitmap fonts.  This is done by using dvips's start-hook to
  73. %%*     overwrite the df-tail and D procedures that dvips uses to
  74. %%*     define its Type 3 bitmap fonts.  Thus, this change should
  75. %%*     provide better support for dvips-generated PS files without
  76. %%*     affecting the handling of other documents.
  77. %%* 
  78. %%* (b) Fixed two bugs that could potentially affect any PS file, not
  79. %%*     just those created by dvips: (1) added missing "get" operator
  80. %%*     in .show.write and (2) fixed bug that caused a hyphen at the
  81. %%*     end of a line to be replaced by a space rather than begin
  82. %%*     deleted.  Note that the first bug was a source of stack
  83. %%*     leakage, causing ps2ascii to run out of operand stack space
  84. %%*     occasionally.
  85. %%* 
  86. %%*     Search for "%%* BF" to find these modifications.
  87. %%*     
  88. %%* (c) Improved the heuristic for determining whether a line break
  89. %%*     has occured and whether a line break represents a paragraph
  90. %%*     break.  Previously, any change in the vertical position caused
  91. %%*     a line break; now a line break is only registered if the
  92. %%*     change is larger than the height of the current font.  This
  93. %%*     means that superscripts, subscripts, and such things as
  94. %%*     shifted accents generated by TeX won't cause line breaks.
  95. %%*     Paragraph-recognition is now done by comparing the indentation
  96. %%*     of the new line to the indentation of the previous line and by
  97. %%*     comparing the vertical distance between the new line and the
  98. %%*     previous line to the vertical distance between the previous
  99. %%*     line and its predecessor.
  100. %%*     
  101. %%* (d) Added a hook for renaming the files where stdout and stderr
  102. %%*     go.
  103. %%* 
  104. %%* In general, my additions or changes to the code are described in
  105. %%* comments beginning with "%%*".  However, there are numerous other
  106. %%* places where I have either re-formatted code or added comments to
  107. %%* the code while I was trying to understand it.  These are usually
  108. %%* not specially marked.
  109. %%* 
  110.  
  111. /QUIET true def
  112. systemdict wcheck { systemdict } { userdict } ifelse begin
  113. /.max where { pop } { /.max { 2 copy lt { exch } if pop } bind def } ifelse
  114. /COMPLEX dup where { pop true } { false } ifelse def
  115. /SIMPLE dup where { pop true } { false } ifelse def
  116. /setglobal where
  117.  { pop currentglobal /setglobal load true setglobal }
  118.  { { } }
  119. ifelse
  120.  
  121. % Define a way to store and retrieve integers that survives save/restore.
  122. /.i.string0 (0               ) def
  123. /.i.string .i.string0 length string def
  124. /.iget { cvi } bind def
  125. /.iput { exch //.i.string exch copy cvs pop } bind def
  126. /.inew { //.i.string0 dup length string copy } bind def
  127.  
  128. % We only want to redefine operators if they are defined already.
  129.  
  130. /codef { 1 index where { pop def } { pop pop } ifelse } def
  131.  
  132. % Redefine the end-of-page operators.
  133.  
  134. /erasepage { } codef
  135. /copypage { SIMPLE { (\014) } { (P\n) } ifelse //print } codef
  136. /showpage { copypage erasepage initgraphics } codef
  137.  
  138. % Redefine the fill operators to detect rectangles.
  139.  
  140. /.orderrect    % <llx> <lly> <urx> <ury> .orderrect <llx> <lly> <w> <h>
  141.  {    % Ensure llx <= urx, lly <= ury.
  142.    1 index 4 index lt { 4 2 roll } if
  143.    dup 3 index lt { 3 1 roll exch } if
  144.    exch 3 index sub exch 2 index sub
  145.  } odef
  146. /.fillcomplex
  147.  {    % Do a first pass to see if the path is all rectangles in
  148.     % the output coordinate system.  We don't worry about overlapping
  149.     % rectangles that might be partially not filled.
  150.     % Stack: mark llx0 lly0 urx0 ury0 ... true mark x0 y0 ...
  151.    mark true mark
  152.     % Add a final moveto so we pick up any trailing unclosed subpath.
  153.    0 0 itransform moveto
  154.     { .coord counttomark 2 gt
  155.        { counttomark 4 gt { .fillcheckrect } { 4 2 roll pop pop }  ifelse }
  156.       if
  157.     }
  158.     { .coord }
  159.     { cleartomark not mark exit }
  160.     { counttomark -2 roll 2 copy counttomark 2 roll .fillcheckrect }
  161.    pathforall cleartomark
  162.     { .showcolor counttomark 4 idiv
  163.        { counttomark -4 roll .orderrect
  164.      (R ) //print .show==4
  165.        }
  166.       repeat pop
  167.     }
  168.     { cleartomark
  169.     }
  170.    ifelse
  171.  } odef
  172. /.fillcheckrect
  173.  {    % Check whether the current subpath is a rectangle.
  174.     % If it is, add it to the list of rectangles being accumulated;
  175.     % if not exit the .fillcomplex loop.
  176.     % The subpath has not been closed.
  177.     % Stack: as in .fillcomplex, + newx newy
  178.    counttomark 10 eq { 9 index 9 index 4 2 roll } if
  179.    counttomark 12 ne { cleartomark not mark exit } if
  180.    12 2 roll
  181.     % Check for the two possible forms of rectangles:
  182.     %    x0 y0  x0 y1  x1 y1  x1 y0  x0 y0
  183.     %    x0 y0  x1 y0  x1 y1  x0 y1  x0 y0
  184.    9 index 2 index eq 9 index 2 index eq and
  185.    10 index 9 index eq
  186.     {    % Check for first form.
  187.       7 index 6 index eq and 6 index 5 index eq and 3 index 2 index eq and
  188.     }
  189.     {    % Check for second form.
  190.       9 index 8 index eq and
  191.       8 index 7 index eq and 5 index 4 index eq and 4 index 3 index eq and
  192.     }
  193.    ifelse not { cleartomark not mark exit } if
  194.     % We have a rectangle.
  195.    pop pop pop pop 4 2 roll pop pop 8 4 roll
  196.  } odef
  197. /eofill { COMPLEX { .fillcomplex } if newpath } codef
  198. /fill { COMPLEX { .fillcomplex } if newpath } codef
  199. /rectfill { gsave newpath .rectappend fill grestore } codef
  200. /ueofill { gsave newpath uappend eofill grestore } codef
  201. /ufill { gsave newpath uappend fill grestore } codef
  202.  
  203. % Redefine the stroke operators to detect rectangles.
  204.  
  205. /rectstroke
  206.  { gsave newpath
  207.    dup type dup /arraytype eq exch /packedarraytype eq or
  208.     { dup length 6 eq { exch .rectappend concat } { .rectappend } ifelse }
  209.     { .rectappend }
  210.    ifelse stroke grestore
  211.  } codef
  212. /.strokeline    % <fromx> <fromy> <tox> <toy> .strokeline <tox> <toy>
  213.         % Note: fromx and fromy are in output coordinates;
  214.         % tox and toy are in user coordinates.
  215.  { .coord 2 copy 6 2 roll .orderrect
  216.     % Add in the line width.  Assume square or round caps.
  217.    currentlinewidth 2 div dup .dcoord add abs 1 max 5 1 roll
  218.    4 index add 4 1 roll 4 index add 4 1 roll
  219.    4 index sub 4 1 roll 5 -1 roll sub 4 1 roll
  220.    (R ) //print .show==4
  221.  } odef
  222. /.strokecomplex
  223.  {    % Do a first pass to see if the path is all horizontal and vertical
  224.     % lines in the output coordinate system.
  225.     % Stack: true mark origx origy curx cury
  226.    true mark null null null null
  227.     { .coord 6 2 roll pop pop pop pop 2 copy }
  228.     { .coord 1 index 4 index eq 1 index 4 index eq or
  229.        { 4 2 roll pop pop }
  230.        { cleartomark not mark exit }
  231.       ifelse
  232.     }
  233.     { cleartomark not mark exit }
  234.     { counttomark -2 roll 2 copy counttomark 2 roll
  235.       1 index 4 index eq 1 index 4 index eq or
  236.        { pop pop 2 copy }
  237.        { cleartomark not mark exit }
  238.       ifelse
  239.     }
  240.    pathforall cleartomark
  241.    0 currentlinewidth .dcoord 0 eq exch 0 eq or and
  242.     % Do the second pass to write out the rectangles.
  243.     % Stack: origx origy curx cury
  244.     { .showcolor null null null null
  245.        { 6 2 roll pop pop pop pop 2 copy .coord }
  246.        { .strokeline }
  247.        { }
  248.        { 3 index 3 index .strokeline }
  249.       pathforall pop pop pop pop
  250.     }
  251.    if
  252.  } odef
  253. /stroke { COMPLEX { .strokecomplex } if newpath } codef
  254. /ustroke
  255.  { gsave newpath
  256.    dup length 6 eq { exch uappend concat } { uappend } ifelse
  257.    stroke grestore
  258.  } codef
  259.  
  260. % The image operators must read the input and note the dimensions.
  261. % Eventually we should redefine these to detect 1-bit-high all-black images,
  262. % since this is how dvips does underlining (!).
  263.  
  264. /.noteimagerect        % <width> <height> <matrix> .noteimagerect -
  265.  { COMPLEX
  266.     { gsave setmatrix itransform 0 0 itransform
  267.       grestore .coord 4 2 roll .coord .orderrect
  268.       (I ) //print .show==4
  269.     }
  270.     { pop pop pop
  271.     }
  272.    ifelse
  273.  } odef
  274. /colorimage where
  275.  { pop /colorimage
  276.     { 1 index
  277.        { dup 6 add index 1 index 6 add index 2 index 5 add index }
  278.        { 6 index 6 index 5 index }
  279.       ifelse .noteimagerect gsave nulldevice //colorimage grestore
  280.     } codef
  281.  } if
  282. /.noteimage        % Arguments as for image[mask]
  283.  { dup type /dicttype eq
  284.     { dup /Width get 1 index /Height get 2 index /ImageMatrix get }
  285.     { 4 index 4 index 3 index }
  286.    ifelse .noteimagerect
  287.  } odef
  288. /image { .noteimage gsave nulldevice //image grestore } codef
  289. /imagemask { .noteimage gsave nulldevice //imagemask grestore } codef
  290.  
  291. % Output the current color if necessary.
  292. /.color.r .inew def
  293.   .color.r -1 .iput        % make sure we write the color at the beginning
  294. /.color.g .inew def
  295. /.color.b .inew def
  296. /.showcolor
  297.  { COMPLEX
  298.     { currentrgbcolor
  299.       1000 mul round cvi
  300.       3 1 roll 1000 mul round cvi
  301.       exch 1000 mul round cvi
  302.         % Stack: b g r
  303.       dup //.color.r .iget eq
  304.       2 index //.color.g .iget eq and
  305.       3 index //.color.b .iget eq and
  306.        { pop pop pop
  307.        }
  308.        { (C ) //print
  309.      dup //.color.r exch .iput .show==only
  310.          ( ) //print dup //.color.g exch .iput .show==only
  311.          ( ) //print dup //.color.b exch .iput .show==only
  312.      (\n) //print
  313.        }
  314.       ifelse
  315.     }
  316.    if
  317.  } bind def
  318.  
  319. % Redefine `show'.
  320.  
  321. % Set things up so our output will be in tenths of a point, with origin at
  322. % lower left.  This isolates us from the peculiarities of individual devices.
  323.  
  324. /.show.ident.matrix matrix def
  325. /.show.ident
  326. % { //.show.ident.matrix defaultmatrix
  327. %               % Assume the original transformation is well-behaved.
  328. %   0.1 0 2 index dtransform abs exch abs .max /.show.scale exch def
  329. %   0.1 dup 3 -1 roll scale
  330.  { gsave initmatrix
  331.                 % Assume the original transformation is well-behaved.
  332.    0.1 0 dtransform abs exch abs .max /.show.scale exch def
  333.    0.1 dup scale .show.ident.matrix currentmatrix
  334.    grestore
  335.  } bind def
  336.  
  337. /.coord
  338.  { transform .show.ident itransform
  339.    exch round cvi exch round cvi
  340.  } odef
  341.  
  342. /.dcoord
  343.  {              % Transforming distances is trickier, because
  344.                 % the coordinate system might be rotated.
  345.    .show.ident pop
  346.    exch 0 dtransform
  347.     dup mul exch dup mul add sqrt
  348.      .show.scale div round cvi
  349.    exch 0 exch dtransform
  350.     dup mul exch dup mul add sqrt
  351.      .show.scale div round cvi
  352.  } odef
  353.  
  354. % Remember the current X, Y, and height.
  355. /.show.x .inew def
  356. /.show.y .inew def
  357. /.show.height .inew def
  358.  
  359. % Remember the last character of the previous string; if it was a
  360. % hyphen preceded by a letter, we didn't output the hyphen.
  361.  
  362. /.show.last (\000) def
  363.  
  364. % Remember the current font.
  365. /.font.name 130 string def
  366. /.font.name.length .inew def
  367. /.font.height .inew def
  368. /.font.width .inew def
  369.  
  370. %%* Also remember indentation of current line and previous vertical
  371. %%* skip
  372.  
  373. /.show.indent .inew def
  374. /.show.dy     .inew def
  375.  
  376. % We have to redirect stdout somehow....
  377.  
  378. /.show.stdout { (%stdout) (w) file } bind def
  379.  
  380. % Make sure writing will work even if a program uses =string.
  381. /.show.string =string length string def
  382. /.show.=string =string length string def
  383. /.show==only
  384.  { //=string //.show.=string copy pop
  385.    dup type /stringtype eq
  386.     { dup length //.show.string length le
  387.        { dup rcheck { //.show.string copy } if
  388.        } if
  389.     } if
  390.    .show.stdout exch write==only
  391.    //.show.=string //=string copy pop
  392.  } odef
  393. /.show==4
  394.  { 4 -1 roll .show==only ( ) //print
  395.    3 -1 roll .show==only ( ) //print
  396.    exch .show==only ( ) //print
  397.    .show==only (\n) //print
  398.  } odef
  399.  
  400. /.showwidth    % Same as stringwidth, but disable COMPLEX so that
  401.         % we don't try to detect rectangles during BuildChar.
  402.  { COMPLEX
  403.     { /COMPLEX false def stringwidth /COMPLEX true def }
  404.     { stringwidth }
  405.    ifelse
  406.  } odef
  407.  
  408. /.showfont    % <string> .showfont <string>
  409.  { gsave
  410.     % Try getting the height and width of the font from the FontBBox.
  411.      currentfont /FontBBox .knownget not { {0 0 0 0} } if
  412.      aload pop      % llx lly urx ury
  413.      exch 4 -1 roll % lly ury urx llx
  414.      sub            % lly ury dx
  415.      3 1 roll exch  % dx ury lly
  416.      sub            % dx dy
  417.      2 copy .max 0 ne
  418.       { currentfont /FontMatrix get dtransform
  419.       }
  420.       {    pop pop
  421.     % Fonts produced by dvips, among other applications, have
  422.     % BuildChar procedures that bomb out when given unexpected
  423.     % characters, and there is no way to determine whether a given
  424.     % character will do this.  So for Type 1 fonts, we measure a
  425.     % typical character ('X'); for others, we punt.
  426.     currentfont /FontType get 1 eq
  427.      { (X) .showwidth pop dup 1.3 mul
  428.      }
  429.      {    % No safe way to get the character size.  Punt.
  430.        0 0
  431.      }
  432.     ifelse
  433.       }
  434.      ifelse .dcoord exch
  435.      currentfont /FontName .knownget not { () } if
  436.      dup type /stringtype ne { //.show.string cvs } if
  437.    grestore
  438.     % Stack: height width fontname
  439.    SIMPLE
  440.     { pop pop //.show.height exch .iput }
  441.     { 2 index //.font.height .iget eq
  442.       2 index //.font.width .iget eq and
  443.       1 index //.font.name 0 //.font.name.length .iget getinterval eq and
  444.        { pop pop pop
  445.        }
  446.        { (F ) //print
  447.      3 -1 roll dup //.font.height exch .iput .show==only ( ) //print
  448.          exch dup //.font.width exch .iput .show==only ( ) //print
  449.      dup length //.font.name.length exch .iput
  450.          //.font.name cvs .show==only (\n) //print
  451.        }
  452.       ifelse
  453.     }
  454.    ifelse
  455.  } odef
  456.  
  457. % Define the letters -- characters which, if they occur followed by a hyphen
  458. % at the end of a line, cause the hyphen and line break to be ignored.
  459. /.letter.chars 100 dict def
  460. mark
  461.   65 1 90 { dup 32 add } for
  462.     counttomark
  463.         { StandardEncoding exch get .letter.chars exch dup put }
  464.     repeat
  465. pop
  466.  
  467. % Define a set of characters which, if they occur at the start of a line,
  468. % are taken as indicating a paragraph break.
  469. /.break.chars 50 dict def
  470. mark
  471.     /bullet /dagger /daggerdbl /periodcentered /section
  472.     counttomark
  473.         { .break.chars exch dup put }
  474.     repeat
  475. pop
  476.  
  477. % Define character translation to ASCII.
  478. % We have to do this for the entire character set.
  479.  
  480. /.char.map 500 dict def
  481.  
  482. /.chars.def { counttomark 2 idiv { .char.map 3 1 roll put } repeat pop } def
  483.  
  484. % Encode the printable ASCII characters.
  485.  
  486. mark 32 1 126
  487.  { 1 string dup 0 4 -1 roll put
  488.    dup 0 get StandardEncoding exch get exch
  489.  }
  490. for .chars.def
  491.  
  492.         % Encode accents.
  493. mark
  494.     /acute      (')
  495.     /caron      (^)
  496.     /cedilla    (,)
  497.     /circumflex (^)
  498.     /dieresis   (")
  499.     /grave      (`)
  500.     /ring       (*)
  501.     /tilde      (~)
  502. .chars.def
  503.  
  504.         % Encode the ISO accented characters.
  505. mark 192 1 255
  506.  { ISOLatin1Encoding exch get =string cvs
  507.    dup 0 1 getinterval 1 index dup length 1 sub 1 exch getinterval
  508.    .char.map 2 index known .char.map 2 index known and
  509.     { .char.map 3 -1 roll get .char.map 3 -1 roll get concatstrings
  510.       .char.map 3 1 roll put
  511.     }
  512.     { pop pop pop
  513.     }
  514.    ifelse
  515.  }
  516. for .chars.def
  517.  
  518. % Encode the remaining standard and ISO alphabetic characters.
  519.  
  520. mark
  521.   /AE (AE) /Eth (DH) /OE (OE) /Thorn (Th)
  522.   /ae (ae) /eth (dh)
  523.   /ffi (ffi) /ffl (ffl) /fi (fi) /fl (fl)
  524.   /germandbls (ss) /oe (oe) /thorn (th)
  525. .chars.def
  526.  
  527. % Encode the other standard and ISO characters.
  528.  
  529. mark
  530.   /brokenbar (|) /bullet (*) /copyright ((C)) /currency (#)
  531.   /dagger (#) /daggerdbl (##) /degree (o) /divide (/) /dotaccent (.)
  532.   /dotlessi (i)
  533.   /ellipsis (...) /emdash (--) /endash (-) /exclamdown (!)
  534.   /florin (f) /fraction (/)
  535.   /guillemotleft (<<) /guillemotright (>>)
  536.   /guilsingleft (<) /guilsingright (>) /hungarumlaut ("") /logicalnot (~)
  537.   /macron (_) /minus (-) /mu (u) /multiply (*)
  538.   /ogonek (,) /onehalf (1/2) /onequarter (1/4) /onesuperior (1)
  539.   /ordfeminine (-a) /ordmasculine (-o)
  540.   /paragraph (||) /periodcentered (*) /perthousand (o/oo) /plusminus (+-)
  541.   /questiondown (?) /quotedblbase (") /quotedblleft (") /quotedblright (")
  542.   /quotesinglbase (,) /quotesingle (') /registered ((R))
  543.   /section ($) /sterling (#)
  544.   /threequarters (3/4) /threesuperior (3) /trademark ((TM)) /twosuperior (2)
  545.   /yen (Y)
  546. .chars.def
  547.  
  548. % Encode a few common Symbol characters.
  549.  
  550. mark
  551.   /asteriskmath (*) /copyrightsans ((C)) /copyrightserif ((C))
  552.   /greaterequal (>=) /lessequal (<=) /registersans ((R)) /registerserif ((R))
  553.   /trademarksans ((TM)) /trademarkserif ((TM))
  554. .chars.def
  555.  
  556. %%* Add a few characters from StandardEncoding and ISOLatin1Encoding
  557. %%* that were missing.
  558.  
  559. mark
  560.     /cent           (c)
  561.     /guilsinglleft  (<)
  562.     /guilsinglright (>)
  563.     /breve          (*)
  564.     /Lslash         (L/)
  565.     /lslash         (l/)
  566. .chars.def
  567.  
  568. %%* Define the OT1Encoding and T1Encoding vectors for use with dvips
  569. %%* files.  Unfortunately, there's no way of telling what font is
  570. %%* really being used within a dvips document, so we can't provide an
  571. %%* appropriate encoding for each individual font.  Instead, we'll
  572. %%* just provide support for the two most popular text encodings, the
  573. %%* OT1 and T1 encodings, and just accept the fact that any font not
  574. %%* using one of those encodings will be rendered as gibberish.
  575. %%* 
  576. %%* OT1 is Knuth's 7-bit encoding for the CMR text fonts, while T1
  577. %%* (aka the Cork encoding) is the 8-bit encoding used by the DC
  578. %%* fonts, a preliminary version of the proposed Extended Computer
  579. %%* Modern fonts.  Unfortunately, T1 is not a strict extension of OT1;
  580. %%* they differ in positions 8#000 through 8#040, 8#074, 8#076, 8#134,
  581. %%* 8#137, 8#173, 8#174, 8#175 and 8#177, so we can't use the same
  582. %%* vector for both.
  583. %%* 
  584. %%* Of course, we also can't reliably tell the difference between an
  585. %%* OT1-encoded font and a T1-encoded font based on the information in
  586. %%* a dvips-created PostScript file.  As a best-guess solution, we'll
  587. %%* use the T1 encoding if the font contains any characters in
  588. %%* positions above 8#177 and the OT1 encoding if it doesn't.
  589.  
  590. /T1Encoding  256 array def
  591.  
  592. /OT1Encoding 256 array def
  593.  
  594. %%* T1Encoding shares a lot with StandardEncoding, so let's start
  595. %%* there.
  596.  
  597. StandardEncoding T1Encoding copy pop
  598.  
  599. /OT1.encode {
  600.     counttomark
  601.     2 idiv
  602.       { OT1Encoding 3 1 roll put }
  603.     repeat
  604.     cleartomark
  605. } def
  606.  
  607. /T1.encode {
  608.     counttomark
  609.     2 idiv
  610.       { T1Encoding 3 1 roll put }
  611.     repeat
  612.     cleartomark
  613. } def
  614.  
  615. mark
  616.     8#000 /grave
  617.     8#001 /acute
  618.     8#002 /circumflex
  619.     8#003 /tilde
  620.     8#004 /dieresis
  621.     8#005 /hungarumlaut
  622.     8#006 /ring
  623.     8#007 /caron
  624.  
  625.     8#010 /breve
  626.     8#011 /macron
  627.     8#012 /dotaccent
  628.     8#013 /cedilla
  629.     8#014 /ogonek
  630.     8#015 /quotesinglbase
  631.     8#016 /guilsinglleft
  632.     8#017 /guilsinglright
  633.  
  634.     8#020 /quotedblleft
  635.     8#021 /quotedblright
  636.     8#022 /quotedblbase
  637.     8#023 /guillemotleft
  638.     8#024 /guillemotright
  639.     8#025 /endash
  640.     8#026 /emdash
  641.     8#027 /cwm
  642.  
  643.     8#030 /perthousandzero
  644.     8#031 /dotlessi
  645.     8#032 /dotlessj
  646.     8#033 /ff
  647.     8#034 /fi
  648.     8#035 /fl
  649.     8#036 /ffi
  650.     8#037 /ffl
  651.  
  652. %%  8#040 through 8#176 follow StandardEncoding
  653.  
  654.     8#177 /hyphen
  655. T1.encode
  656.  
  657. mark
  658.     8#200 /Abreve
  659.     8#201 /Aogonek
  660.     8#202 /Cacute
  661.     8#203 /Ccaron
  662.     8#204 /Dcaron
  663.     8#205 /Ecaron
  664.     8#206 /Eogonek
  665.     8#207 /Gbreve
  666.     8#210 /Lacute
  667.     8#211 /Lcaron
  668.     8#212 /Lslash
  669.     8#213 /Nacute
  670.     8#214 /Ncaron
  671.     8#215 /Eng
  672.     8#216 /Ohungarumlaut
  673.     8#217 /Racute
  674.     8#220 /Rcaron
  675.     8#221 /Sacute
  676.     8#222 /Scaron
  677.     8#223 /Scedilla
  678.     8#224 /Tcaron
  679.     8#225 /Tcedilla
  680.     8#226 /Uhungarumlaut
  681.     8#227 /Uring
  682.     8#230 /Ydieresis
  683.     8#231 /Zacute
  684.     8#232 /Zcaron
  685.     8#233 /Zdot
  686.     8#234 /IJ
  687.     8#235 /Idot
  688.     8#236 /dbar
  689.     8#237 /section
  690.     8#240 /abreve
  691.     8#241 /aogonek
  692.     8#242 /cacute
  693.     8#243 /ccaron
  694.     8#244 /dcaron
  695.     8#245 /ecaron
  696.     8#246 /eogonek
  697.     8#247 /gbreve
  698.     8#250 /lacute
  699.     8#251 /lcaron
  700.     8#252 /lslash
  701.     8#253 /nacute
  702.     8#254 /ncaron
  703.     8#255 /eng
  704.     8#256 /ohungarumlaut
  705.     8#257 /racute
  706.     8#260 /rcaron
  707.     8#261 /sacute
  708.     8#262 /scaron
  709.     8#263 /scedilla
  710.     8#264 /tcaron
  711.     8#265 /tcedilla
  712.     8#266 /uhungarumlaut
  713.     8#267 /uring
  714.     8#270 /ydieresis
  715.     8#271 /zacute
  716.     8#272 /zcaron
  717.     8#273 /zdot
  718.     8#274 /ij
  719.     8#275 /exclamdown
  720.     8#276 /questiondown
  721.     8#277 /sterling
  722.  
  723.     8#300 /Agrave
  724.     8#301 /Aacute
  725.     8#302 /Acircumflex
  726.     8#303 /Atilde
  727.     8#304 /Adieresis
  728.     8#305 /Aring
  729.     8#306 /AE
  730.     8#307 /Ccedilla
  731.     8#310 /Egrave
  732.     8#311 /Eacute
  733.     8#312 /Ecircumflex
  734.     8#313 /Edieresis
  735.     8#314 /Igrave
  736.     8#315 /Iacute
  737.     8#316 /Icircumflex
  738.     8#317 /Idieresis
  739.     8#320 /Eth
  740.     8#321 /Ntilde
  741.     8#322 /Ograve
  742.     8#323 /Oacute
  743.     8#324 /Ocircumflex
  744.     8#325 /Otilde
  745.     8#326 /Odieresis
  746.     8#327 /OE
  747.     8#330 /Oslash
  748.     8#331 /Ugrave
  749.     8#332 /Uacute
  750.     8#333 /Ucircumflex
  751.     8#334 /Udieresis
  752.     8#335 /Yacute
  753.     8#336 /Thorn
  754.     8#337 /Germandbls 
  755.  
  756.     8#340 /agrave
  757.     8#341 /aacute
  758.     8#342 /acircumflex
  759.     8#343 /atilde
  760.     8#344 /adieresis
  761.     8#345 /aring
  762.     8#346 /ae
  763.     8#347 /ccedilla
  764.     8#350 /egrave
  765.     8#351 /eacute
  766.     8#352 /ecircumflex
  767.     8#353 /edieresis
  768.     8#354 /igrave
  769.     8#355 /iacute
  770.     8#356 /icircumflex
  771.     8#357 /idieresis
  772.     8#360 /eth
  773.     8#361 /ntilde
  774.     8#362 /ograve
  775.     8#363 /oacute
  776.     8#364 /ocircumflex
  777.     8#365 /otilde
  778.     8#366 /odieresis
  779.     8#367 /oe
  780.     8#370 /oslash
  781.     8#371 /ugrave
  782.     8#372 /uacute
  783.     8#373 /ucircumflex
  784.     8#374 /udieresis
  785.     8#375 /yacute
  786.     8#376 /thorn
  787.     8#377 /germandbls
  788.  
  789. T1.encode
  790.  
  791. %%* Now copy OT1Encoding into T1Encoding and make a few changes.
  792.  
  793. T1Encoding OT1Encoding copy pop
  794.  
  795. mark
  796.     8#000 /Gamma
  797.     8#001 /Delta
  798.     8#002 /Theta
  799.     8#003 /Lambda
  800.     8#004 /Xi
  801.     8#005 /Pi
  802.     8#006 /Sigma
  803.     8#007 /Upsilon
  804.  
  805.     8#010 /Phi
  806.     8#011 /Psi
  807.     8#012 /Omega
  808.     8#013 /ff
  809.     8#014 /fi
  810.     8#015 /fl
  811.     8#016 /ffi
  812.     8#017 /ffl
  813.  
  814.     8#020 /dotlessi
  815.     8#021 /dotlessj
  816.     8#022 /grave
  817.     8#023 /acute
  818.     8#024 /caron
  819.     8#025 /breve
  820.     8#026 /macron
  821.     8#027 /ring
  822.  
  823.     8#030 /cedilla
  824.     8#031 /germandbls
  825.     8#032 /ae
  826.     8#033 /oe
  827.     8#034 /oslash
  828.     8#035 /AE
  829.     8#036 /OE
  830.     8#037 /Oslash
  831.  
  832.     8#040 /polishslash
  833.  
  834.     8#042 /quotedblright
  835.  
  836.     8#074 /exclamdown
  837.     8#076 /questiondown
  838.  
  839.     8#134 /quotedblleft
  840.     8#137 /dotaccent
  841.  
  842.     8#173 /endash
  843.     8#174 /emdash
  844.     8#175 /hungarumlaut
  845.     8#177 /dieresis
  846. OT1.encode
  847.  
  848. %%* And add a few characters from the OT1Encoding
  849.  
  850. mark
  851.     /Gamma              (\\Gamma )
  852.     /Delta              (\\Delta )
  853.     /Theta              (\\Theta )
  854.     /Lambda             (\\Lambda )
  855.     /Xi                 (\\Xi )
  856.     /Pi                 (\\Pi )
  857.     /Sigma              (\\Sigma )
  858.     /Upsilon            (\\Upsilon )
  859.  
  860.     /Phi                (\\Phi )
  861.     /Psi                (\\Psi )
  862.     /Omega              (\\Omega )
  863.  
  864.     /dotlessj           (j)
  865.     /ff                 (ff)
  866.  
  867.     /cwm                ()
  868.  
  869.     /perthousandzero    (0)
  870.  
  871.     /polishslash        ()
  872.  
  873.     /Abreve             (A*)
  874.     /Aogonek            (A,)
  875.     /Cacute             (C')
  876.     /Ccaron             (C^)
  877.     /Dcaron             (D^)
  878.     /Ecaron             (E^)
  879.     /Eogonek            (E,)
  880.     /Gbreve             (G*)
  881.     /Lacute             (L')
  882.     /Lcaron             (L^)
  883.     /Nacute             (N')
  884.     /Ncaron             (N^)
  885.     /Eng                (NG)
  886.     /Ohungarumlaut      (O"")
  887.     /Racute             (R')
  888.     /Rcaron             (R^)
  889.     /Sacute             (S')
  890.     /Scaron             (S^)
  891.     /Scedilla           (S,)
  892.     /Tcaron             (T^)
  893.     /Tcedilla           (T,)
  894.     /Uhungarumlaut      (U"")
  895.     /Uring              (U*)
  896.     /Ydieresis          (Y")
  897.     /Zacute             (Z')
  898.     /Zcaron             (Z^)
  899.     /Zdot               (Z.)
  900.     /IJ                 (IJ)
  901.     /Idot               (I.)
  902.     /dbar               (d-)
  903.     /abreve             (a*)
  904.     /aogonek            (a,)
  905.     /cacute             (c')
  906.     /ccaron             (c^)
  907.     /dcaron             (d^)
  908.     /ecaron             (e^)
  909.     /eogonek            (e,)
  910.     /gbreve             (g*)
  911.     /lacute             (l')
  912.     /lcaron             (l^)
  913.     /nacute             (n')
  914.     /ncaron             (n^)
  915.     /eng                (ng)
  916.     /ohungarumlaut      (o"")
  917.     /racute             (r')
  918.     /rcaron             (r^)
  919.     /sacute             (s')
  920.     /scaron             (s^)
  921.     /scedilla           (s,)
  922.     /tcaron             (t^)
  923.     /tcedilla           (t,)
  924.     /uhungarumlaut      (u"")
  925.     /uring              (u*)
  926.     /zacute             (z')
  927.     /zcaron             (z^)
  928.     /zdot               (z.)
  929.     /ij                 (ij)
  930.     /Germandbls         (SS)
  931. .chars.def
  932.  
  933. %%* We extend the df-tail command to stick in an Encoding vector (see
  934. %%* above for a discussion of the T1 and OT1 encodings), put in a
  935. %%* FontName (which will just be dvips's name for the font, i.e., Fa,
  936. %%* Fb, etc.) and give each font a separate FontBBox instead of
  937. %%* letting them all share a single one.
  938.  
  939. /dvips.df-tail      % id numcc maxcc df-tail
  940.   {
  941.     /nn 9 dict N
  942.     nn begin
  943.         %%  
  944.         %%  Choose an encoding based on the highest position occupied.
  945.         %%  
  946.         dup 127 gt { T1Encoding } { OT1Encoding } ifelse
  947.         /Encoding X
  948.         /FontType 3 N
  949.         %%
  950.         %%  It's ok for all the fonts to share a FontMatrix, but they
  951.         %%  need to have separate FontBBoxes
  952.         %%
  953.     /FontMatrix fntrx N
  954.     /FontBBox [0 0 0 0] N
  955.         string /base X
  956.         array /BitMaps X
  957.         %%
  958.         %%  And let's throw in a FontName for good measure
  959.         %%
  960.         dup (    ) cvs /FontName X
  961.         /BuildChar {CharBuilder} N
  962.     end
  963.     dup { /foo setfont }
  964.        2 array copy cvx N
  965.     load
  966.        0 nn put
  967.     /ctr 0 N
  968.     [
  969. } def
  970.  
  971. %%* This is functionally equivalent to dvips's /D procedure, but it
  972. %%* also calculates the Font Bounding Box while defining the
  973. %%* characters.
  974.  
  975. /dvips.D   % char-data ch D - : define character bitmap in current font
  976. {
  977.     /cc X                           % char-data
  978.     dup type /stringtype ne {]} if  % char-data
  979.  
  980.     /ch-data X
  981.     nn /base get cc ctr put     % (adds ctr to cc'th position of BASE)
  982.     nn /BitMaps get
  983.     ctr
  984.     ch-data                     % BitMaps ctr char-data
  985.     sf 1 ne {
  986.        dup dup length 1 sub dup 2 index S get sf div put
  987.     } if
  988.     put                         % puts char-data into BitMaps at index ctr
  989.     /ctr ctr 1 add N
  990. %%  
  991. %%  Make sure the Font Bounding Box encloses the Bounding Box of the
  992. %%  current character
  993. %%
  994.     nn /FontBBox get        % BB
  995.  
  996.     dup                     % calculate new llx
  997.     dup 0 get
  998.     ch-xoff
  999.     min
  1000.     0 exch put
  1001.  
  1002.     dup                     % calculate new lly
  1003.     dup 1 get
  1004.     ch-yoff ch-height sub
  1005.     min
  1006.     1 exch put
  1007.  
  1008.     dup                     % calculate new urx
  1009.     dup 2 get
  1010.     ch-dx ch-width add
  1011.     max
  1012.     2 exch put
  1013.  
  1014.     dup 3 get               % calculate new ury
  1015.     ch-yoff
  1016.     max
  1017.     3 exch put
  1018.  
  1019. } def
  1020.  
  1021. %%* Define start-hook to replace df-tail and D by our versions.
  1022. %%* Unfortunately, the user can redefine start-hook and thus bypass
  1023. %%* these changes, but I don't see an obvious way around that.
  1024.  
  1025. userdict /start-hook {
  1026.     TeXDict /df-tail /dvips.df-tail load bind put
  1027.     TeXDict /D       /dvips.D       load bind put
  1028. } put
  1029.  
  1030. %%* Introduce a symbolic constant for hyphens.  (Need to make
  1031. %%* allowance for hyphen being in different place?)
  1032.  
  1033. /.hyphen 45 def
  1034.  
  1035. % Write out a string.  If it ends in a letter and a hyphen,
  1036. % don't write the hyphen, and set .show.last to a hyphen;
  1037. % otherwise, set .show.last to the character (or \000 if it was a hyphen).
  1038. /.show.write    % <string>
  1039.  {
  1040.     dup length 1 ge
  1041.         { dup dup length 1 sub get      % string last_char
  1042.           dup .hyphen eq                % string last_char hyphen?
  1043.             {                           % string last_char
  1044.                 1 index length 1 gt
  1045.                     { 1 index dup length 2 sub get }
  1046.                     { //.show.last 0 get }
  1047.                 ifelse                  % string last_char prev-char
  1048.                 currentfont /Encoding get exch get  % look up prev-char
  1049.                 //.letter.chars exch known          % is it a letter?
  1050.                     { % Remove the hyphen           % string last_char
  1051.                         exch                        % last_char string
  1052.                         dup length 1 sub            % last_char string len-1
  1053.                         0 exch getinterval          % last_char string-1
  1054.                         exch                        % string-1 last_char
  1055.                     }
  1056.                     { pop 0 }                       % string 0
  1057.                 ifelse
  1058.             }
  1059.           if
  1060.           //.show.last 0 3 -1 roll put              % store last_char
  1061.                                                     % in .show.last
  1062.                                                     % If .show.last ==
  1063.                                                     % hyphen, then
  1064.                                                     % last char of
  1065.                                                     % previous string
  1066.                                                     % was a hyphen
  1067.         }
  1068.     if                                          % string
  1069.     { % begin forall                            % c
  1070.       dup currentfont /Encoding get exch get    % c enc
  1071.       dup //.char.map exch .knownget            % c enc str bool
  1072.         {                                       % c enc str
  1073.           .show.stdout exch writestring pop pop
  1074.         }
  1075.         {                                       % c enc (c not in .char.map)
  1076.           currentfont /Encoding get             %%* BF1: added missing "get"
  1077.           dup OT1Encoding eq                    %%* added OT1Encoding
  1078.           exch                      % c bool enc
  1079.           dup T1Encoding eq                     %%* added T1Encoding
  1080.           exch                      % c bool enc
  1081.           dup StandardEncoding eq   % c bool enc bool
  1082.           exch
  1083.           ISOLatin1Encoding eq
  1084.           or
  1085.           or
  1086.             { % Untranslated character in standard encoding ("normal" case)
  1087.                 pop .show.stdout exch write
  1088.             }
  1089.             { % Character in non-standard encoding, substitute
  1090.                 pop pop .show.stdout (*) writestring
  1091.             }
  1092.           ifelse
  1093.         }
  1094.       ifelse
  1095.     }
  1096.     forall
  1097. } odef
  1098.  
  1099. /.showstring1 {                 % string
  1100.     currentpoint .coord         % string x y
  1101.     3 -1 roll dup .showwidth    % x y string dx dy
  1102.     1 index                     % x y string dx dy dx
  1103.     0 rmoveto                   % x y string dx dy
  1104.     .dcoord pop                 % x y string width
  1105.     SIMPLE
  1106.       {                         % x y string width
  1107.         2 index                 % x y string width y
  1108.         //.show.y .iget         % x y string width y old.y
  1109.         %%* 
  1110.         %%* Replaced test "has y changed" by "has y changed by more
  1111.         %%* than the current font height" so that subscripts and
  1112.         %%* superscripts won't cause line/paragraph breaks
  1113.         %%* 
  1114.          sub abs dup            % x y string width dy dy
  1115.          //.show.height .iget
  1116.          gt
  1117.          {                      % x y string width dy
  1118.  
  1119.             %%* Vertical position has changed by more than the font
  1120.             %%* height, so we now try to figure out whether we've
  1121.             %%* started a new paragraph or merely a new line, using a
  1122.             %%* variety of heuristics.
  1123.  
  1124.             %%* If any of the following is true, we start a new
  1125.             %%* paragraph:
  1126.  
  1127.             %%* (a) the current vertical shift is more than 1.1 times
  1128.             %%*     the previous vertical shift, where 1.1 is an
  1129.             %%*     arbitrarily chosen factor that could probably be
  1130.             %%*     refined.
  1131.  
  1132.             dup                 % x y string width dy dy
  1133.             //.show.dy .iget 1.1 mul
  1134.             gt
  1135.             exch
  1136.  
  1137.             %%* Save the new vertical shift
  1138.  
  1139.             //.show.dy exch .iput
  1140.  
  1141.             %%* (b) The vertical shift is more than 1.3 times the
  1142.             %%*     "size" of the current font.  I've removed this
  1143.             %%*     test since it's not really very useful.
  1144.  
  1145. %%*            //.show.dy .iget
  1146. %%*            //.show.height .iget 1.4 mul
  1147. %%*            gt                          % x y string width bool
  1148. %%*            .show.height .iget 0 gt and % only perform test if font
  1149. %%*                                        % height is nonzero
  1150. %%*            or
  1151.  
  1152.             %%* (c) the first character of the new line is one of the
  1153.             %%*     .break.chars
  1154.  
  1155.             2 index length      % x y string width newpar? len
  1156.             0 gt                % x y string width newpar? len>0?
  1157.               {
  1158.                 2 index 0 get   % x y string width newpar? s
  1159.                 currentfont /Encoding get
  1160.                 exch get        % x y string width newpar? s_enc
  1161.                 //.break.chars exch known { pop true } if
  1162.               }
  1163.             if                  % x y string width newpar?
  1164.  
  1165.             %%* (d) The indentation of the new line is greater than
  1166.             %%*     the indentation of the previous line.
  1167.  
  1168.             4 index
  1169.             //.show.indent .iget
  1170.             gt
  1171.             or
  1172.  
  1173.             % newpar?
  1174.               { (\n\n) }        % Paragraph
  1175.               {                 % Line
  1176.                                 %%* 
  1177.                                 %%* BF2: If last character on a line is
  1178.                                 %%* a hyphen, we omit the hyphen and
  1179.                                 %%* run the lines together.  Of
  1180.                                 %%* course, this will fail if a word
  1181.                                 %%* with an explicit hyphen (e.g.,
  1182.                                 %%* X-ray) is split across two lines.
  1183.                                 %%* Oh, well.  (What should we do
  1184.                                 %%* about a hyphen that ends a
  1185.                                 %%* "paragraph"?  Perhaps that should
  1186.                                 %%* inhibit a paragraph break.)
  1187.                                 %%*
  1188.                 //.show.last 0 get .hyphen eq
  1189.                     { ()  }
  1190.                     { ( ) }
  1191.                 ifelse          % x y string width char
  1192.               }
  1193.             ifelse
  1194.             //print
  1195.  
  1196.             //.show.y 3 index .iput % x y string width
  1197.             //.show.x 4 index .iput % x y string width
  1198.             //.show.indent 4 index .iput
  1199.          }
  1200.          {                      % x y string width dy
  1201.                   % If the word processor split a hyphenated word within
  1202.                   % the same line, put out the hyphen now.
  1203.             pop
  1204.             //.show.last 0 get .hyphen eq { (-) //print } if
  1205.          }
  1206.         ifelse
  1207.                                 %%* 
  1208.                                 %%* If have moved more than 1 point to
  1209.                                 %%* the right, interpret it as a
  1210.                                 %%* space?  This need to be looked at
  1211.                                 %%* more closely.
  1212.                                 %%* 
  1213.         3 index                     % x y string width x
  1214.         //.show.x .iget 10 add gt   % x y string width bool
  1215.             { ( ) //print }
  1216.         if
  1217.                                     % x y string width
  1218.         4 1 roll                    % width x y string
  1219.         .show.write pop             % width x
  1220.         add //.show.x exch .iput    % <empty>
  1221.       }
  1222.       { (S ) //print .show==4 }
  1223.     ifelse
  1224. } odef
  1225.  
  1226. /.showstring
  1227.  { dup () eq { pop } { .showstring1 } ifelse
  1228.  } bind def
  1229.  
  1230. % Redefine all the string display operators.
  1231.  
  1232. /show {
  1233.     .showfont
  1234.     .showcolor
  1235.     .showstring
  1236. } codef
  1237.  
  1238. % We define all the other operators in terms of .show1.
  1239.  
  1240. /.show1.string ( ) def
  1241. /.show1 { //.show1.string exch 0 exch put //.show1.string .showstring } odef
  1242. /ashow
  1243.  { .showfont .showcolor
  1244.    { .show1 2 copy rmoveto } forall
  1245.    pop pop
  1246.  } codef
  1247. /awidthshow
  1248.  { .showfont .showcolor
  1249.     { dup .show1 4 index eq { 4 index 4 index rmoveto } if
  1250.       2 copy rmoveto
  1251.     }
  1252.    forall
  1253.    pop pop pop pop pop
  1254.  } codef
  1255. /widthshow
  1256.  { .showfont .showcolor
  1257.    //.show1.string 0 4 -1 roll put
  1258.     { //.show1.string search not { exit } if
  1259.       .showstring .showstring
  1260.       2 index 2 index rmoveto
  1261.     } loop
  1262.    .showstring pop pop
  1263.  } codef
  1264. /kshow
  1265.  { .showfont .showcolor
  1266.     %**************** Should construct a closure, in case the procedure
  1267.     %**************** affects the o-stack.
  1268.     { .show1 dup exec } forall pop
  1269.  } codef
  1270.  
  1271. % We don't really do the right thing with the Level 2 show operators,
  1272. % but we do something semi-reasonable.
  1273. /xshow { pop show } codef
  1274. /yshow { pop show } codef
  1275. /xyshow { pop show } codef
  1276. /glyphshow
  1277.  { currentfont /Encoding .knownget not { {} } if
  1278.    0 1 2 index length 1 sub
  1279.     {        % Stack: glyph encoding index
  1280.       2 copy get 3 index eq { exch pop exch pop null exit } if
  1281.     }
  1282.    for null eq { (X) dup 0 4 -1 roll put show } { pop } ifelse
  1283.  } codef
  1284.  
  1285. end
  1286.  
  1287. % Bind the operators we just defined, and all the others if we didn't
  1288. % do it before.  Also reenable 'bind' for future files.
  1289.  
  1290. .bindoperators
  1291. NOBIND currentdict systemdict ne and
  1292.  { systemdict begin .bindoperators end }
  1293. if
  1294. NOBIND
  1295.  { /bind /.bind load def }
  1296. if
  1297.  
  1298. % Make systemdict read-only if it wasn't already.
  1299.  
  1300. systemdict wcheck { systemdict readonly pop } if
  1301.  
  1302. % Restore the current local/global VM mode.
  1303.  
  1304. exec
  1305.  
  1306.  
  1307.